<template>
  <section class="component data-tree-node">
    <div :style="getSubTreeStyle()">
      <div @click="onNodeClick" class="node-title" :class="{selected:selected}">
        <span @click="onExpand" v-if="!isLeaf" :class="{expanded:expanded}">
          <i v-show="loading" class="el-icon-loading"></i>
          <i v-show="!loading" :class="expanded ? 'el-icon-caret-bottom' :'el-icon-caret-right'"></i>
        </span>
        <span v-if="showCheckbox">
          <i-checkbox :indeterminate="indeterminate" v-model="checked" @on-change="onChecked"></i-checkbox>
        </span>
        <span>{{data[props.title]}}</span>
        <small v-show="selected && showEdit" class="icon-box">
          <a @click="editHandle" href="#" :disabled="disabled" v-if="hasEdit">
            <svg-icon iconName="xiugai"></svg-icon>
          </a>
          <a @click="addItemHandle" href="#" :disabled="disabled" v-if="hasAdd">
            <svg-icon iconName="zengjia"></svg-icon>
          </a>
          <a @click="deleteHandle" href="#" :disabled="disabled" v-if="hasDelete">
            <svg-icon iconName="guanbi"></svg-icon>
          </a>
        </small>
      </div>
      <div v-show="expanded" class="sub-node" v-if="data.children">
        <data-tree-node ref="children-node" v-for="node in data.children" :key="node.id" :data="node"></data-tree-node>
      </div>
    </div>
  </section>
</template>

<script lang="ts">
import Vue from "vue";
import Component from "vue-class-component";
import { Prop, Watch } from "vue-property-decorator";
import DataTree from "~/components/common/data-tree.vue";
import { EditType } from "~/config/enum.framework";
import SvgIcon from "~/components/common/svg-icon.vue";

@Component({
  name: "data-tree-node",
  components: {
    SvgIcon
  }
})
export default class DataTreeNode extends Vue {
  public expanded = false;
  public selected = false;
  public indeterminate = false
  // 加载中
  private loading = false;
  private root: any = {};
  @Prop({
    required: true
  })
  private data;




  private get props() {
    return this.root.propsObject
  }

  get checked() {
    return this.data._checked
  }

  set checked(value) {
    this.data._checked = value
  }

  get showCheckbox() {
    return this.root.showCheckbox;
  }

  /** 是否禁用操作项 */
  get disabled() {
    return this.data._disabled || false
  }

  get showEdit() {
    return this.root.showEdit;
  }

  get hasAdd() {
    return this.root.editConfig.includes(EditType.ADD)
  }

  get hasEdit() {
    return this.root.editConfig.includes(EditType.MODIFY)
  }

  get hasDelete() {
    return this.root.editConfig.includes(EditType.DELETE)
  }


  /**
   * 调用树的编辑方法，向调用者发送edit事件
   */
  private editHandle() {
    this.root.emitEdit(this.data);
  }
  /**
   * 点击树添加事件
   */
  private addItemHandle() {
    this.root.addEdit(this.data);
  }
  /**
   * 点击树删除事件
   */
  private deleteHandle() {
    this.root.deleteEdit(this.data)
  }
  /**
   * 点击当前节点
   */
  private currentNode() {
    this.root.clickNode(this.data)
  }

  /**
   * 当前节点更新操作
   * 保证更新顺序
   */
  private onChecked(value) {
    // 取消自动关联状态
    this.indeterminate = false;
    // step1: 更新子节点
    this.updateChildrenChecked(value)
    // step2: 更新父节点
    this.updateParentChecked(value)

    // 发送节点更新事件
    this.root.emitCurrentCheckedChange(this.data.id, value)

  }

  /**
   * 更新父节点选中状态
   */
  private updateParentChecked(value) {
    // 获取父节点
    const parent = this.getParent()

    if (parent) {
      // 获取目标状态
      const targetChecked = parent.data.children.some(x => x._checked === true)
      // 更新半选状态
      parent.indeterminate = targetChecked && !parent.data.children.every(x => x._checked === true)

      // 如果状态发生变化则更新父节点
      if (parent.checked !== targetChecked) {
        parent.checked = targetChecked
        parent.updateParentChecked()
      }
    }
  }

  /**
   * 更新子节点选中状态
   */
  private updateChildrenChecked(value: boolean) {
    if (this.data.children && this.data.children.length) {
      // 获取子节点组件
      const nodes = this.$refs['children-node'] as DataTreeNode[];
      // 通知子组件更新
      nodes.forEach(item => {
        item.checked = value
        // 发送节点更新事件
        this.root.emitCurrentCheckedChange(item.data.id, value)
        item.updateChildrenChecked(value)
      })
    }
  }

  /**
   * 是否叶子节点 或者 懒加载模式
   */
  private get isLeaf() {
    if (this.root.loadData !== undefined) {
      if (this.data.loading) {
        return !this.HasChildren
      } else {
        return false
      }
    } else {
      return !this.HasChildren
    }
  }

  /**
   * 是否有children数据
   */
  private get HasChildren() {
    return this.data.children
  }

  private getSubTreeStyle() {
    return {
      marginLeft: this.isLeaf ? "15px" : "10px",
      marginTop: "3px"
    };
  }

  /**
   * 节点展开处理
   */
  private onExpand() {
    if (!this.expanded) {
      if (this.root.loadData !== undefined && !this.data.loading) {
        // 将加载后的子数据给当前节点
        this.loading = true
        const load = this.root.loadData(this.data);
        if (load) {
          (load as Promise<any>).then(data => {
            this.data.children = data
            this.data.loading = true
            this.loading = false
          })
        } else {
          this.loading = false
        }
      }
    }
    this.expanded = !this.expanded;
  }

  /**
   * 节点选择处理
   */
  private onNodeClick() {
    this.selected = true
    this.root.selected = this
  }

  private getParent(target = "data-tree-node") {
    let parent = this.$parent;

    // 向上寻找根节点元素
    while (parent && parent.$options && parent.$options.name !== target) {
      parent = parent.$parent;
    }

    return parent as any;
  }

  private created() {
    this.root = this.getParent('data-tree')
    this.expanded = this.data.expanded
  }

  private mounted() {
    this.root.registerNode(this, this.data)
    this.checked = !!this.data._checked
    if (this.data._selected) this.onNodeClick()
  }


}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="less" scoped>
.component.data-tree-node {
  text-align: left;
  font-size: 14px;

  .node-title {
    &:hover {
      cursor: pointer;
    }
    &.selected {
      background-color: #d5e8fc;
    }
  }
  .icon-box {
    padding-left: 10px;
    > a {
      margin: 0 2px;
    }
  }
}
</style>
